import serial
import threading
from PySide6.QtCore import QTimer,QObject,Signal
import time
import serial.tools.list_ports
from PySide6 import QtWidgets
from PySide6.QtWidgets import QMessageBox
from ui_adcsample import Ui_Form
import re
import csv

class PySide6_Serial(QtWidgets.QWidget, Ui_Form):
    parity_map = {"None":"N","Odd":"O","Even":"E"}
    times_map = {"voltage":392.16,"current":5,"charge":110}
    def __init__(self):
        super(PySide6_Serial, self).__init__()
        self.setupUi(self)
        self.init()
        # self.setWindowTitle("串口小助手")
        self.ser = serial.Serial()
        self.port_check()
        self.parsed_data =[]
        self.pattern = re.compile(r'\d+')
        self.data_lock = threading.Lock()
        self.sample_rate = 10
        self.sample_temp_count = 0
        self.mode = "voltage" # 0:voltage, 1:current, 2:charge
        self.times = 392.16
        self.sprate = 1024
        
        # 接收数据和发送数据数目置零
        self.data_num_received = 0
        # self.lineEdit.setText(str(self.data_num_received))
        self.data_num_sended = 0
        # self.lineEdit_2.setText(str(self.data_num_sended))

    def init(self):
        # 串口检测按钮
        self.button_detect.clicked.connect(self.port_check)

        # 串口信息显示
        # self.combo_device.currentTextChanged.connect(self.port_imf)

        # 打开串口按钮
        self.button_open.clicked.connect(self.port_open)

        # 关闭串口按钮
        self.button_close.clicked.connect(self.port_close)
        self.button_save.clicked.connect(self.trigger_save_data)
        
        self.combo_sample_rate.currentIndexChanged.connect(self.trigger_change_sample_rate)
        self.combo_mode.currentIndexChanged.connect(self.trigger_change_mode)
        self.combo_times.currentIndexChanged.connect(self.trigger_change_times)
    
        # 发送数据按钮
        # self.s3__send_button.clicked.connect(self.data_send)

        # 定时发送数据
        # self.timer_send = QTimer()
        # self.timer_send.timeout.connect(self.data_send)
        # self.timer_send_cb.stateChanged.connect(self.data_send_timer)

        # 定时器接收数据
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.data_receive)

        # 清除发送窗口
        # self.s3__clear_button.clicked.connect(self.send_data_clear)

        # 清除接收窗口
        # self.s2__clear_button.clicked.connect(self.receive_data_clear)

    # 串口检测
    def port_check(self):
        # 检测所有存在的串口，将信息存储在字典中
        self.Com_Dict = {}
        port_list = list(serial.tools.list_ports.comports())
        self.combo_device.clear()
        for port in port_list:
            self.Com_Dict[f"{port[0]}:{port[1]}"] = "%s" % port[0]
            self.combo_device.addItem(f"{port[0]}:{port[1]}")
        # if len(self.Com_Dict) == 0:
            # self.state_label.setText(" 无串口")

    # 串口信息
    # def port_imf(self):
        # 显示选定的串口的详细信息
        # imf_s = self.s1__box_2.currentText()
        # if imf_s != "":
            # self.state_label.setText(self.Com_Dict[self.s1__box_2.currentText()])

    # 打开串口
    def port_open(self):
        self.cur_time = time.perf_counter()
        self.ser.port = self.Com_Dict[self.combo_device.currentText()]
        self.ser.baudrate = int(self.combo_baudrate.currentText())
        self.ser.bytesize = int(self.combo_databits.currentText())
        self.ser.stopbits = int(self.combo_stopbits.currentText())
        self.ser.parity = PySide6_Serial.parity_map[self.combo_parity.currentText()]
        print(f"port:{self.ser.port}, baudrate:{self.ser.baudrate}, bytesize:{self.ser.bytesize}, stopbits:{self.ser.stopbits}, parity:{self.ser.parity}")
        try:
            self.ser.open()
        except:
            QMessageBox.critical(self, "Port Error", "此串口不能被打开！")
            return None

        # 打开串口接收定时器，周期为2ms
        self.timer.start(10)

        if self.ser.isOpen():
            self.button_open.setEnabled(False)
            self.button_close.setEnabled(True)
            # self.formGroupBox1.setTitle("串口状态（已开启）")

    # 关闭串口
    def port_close(self):
        self.timer.stop()
        # self.timer_send.stop()
        try:
            self.ser.close()
        except:
            pass
        self.button_open.setEnabled(True)
        self.button_close.setEnabled(False)
        # self.lineEdit_3.setEnabled(True)
        # 接收数据和发送数据数目置零
        self.data_num_received = 0
        # self.lineEdit.setText(str(self.data_num_received))
        self.data_num_sended = 0
        # self.lineEdit_2.setText(str(self.data_num_sended))
        # self.formGroupBox1.setTitle("串口状态（已关闭）")
    # 发送数据
    # 接收数据
    def data_receive(self):
        try:
            num = self.ser.inWaiting()
            # num_temp = num
        except:  
            self.port_close()
            return None
        if num > 0:
            while num >= 3:
                data = self.ser.readline()
                if len(data) == 3:
                    self.sample_temp_count +=1
                    if(self.sample_temp_count >= self.sample_rate):
                        self.sample_temp_count = 0
                        adc_value = int(data[1]<<8|data[0])  # 提取ADC值
                        # data = data.decode('utf-8',errors='ignore')
                        # strdata += data
                        adc_value = round(adc_value * 3.3 / 4096 - 1.65, 4) * self.times
                        with self.data_lock:  # 确保线程安全
                            self.parsed_data.append(adc_value)
                            if len(self.parsed_data) > self.sprate:
                                self.parsed_data.pop(0)
                num -=len(data)
            # if self.checkbox_hexdisplay.isChecked():
                # out_s = ' '.join('{:02X}'.format(ord(byte)) for byte in strdata)+" "
                # self.textbox_receive.insertPlainText(out_s)
            # else:
                # 串口接收到的字符串为b'123',要转化成unicode字符串才能输出到窗口中去
                # self.textbox_receive.insertPlainText(strdata)

            # 统计接收字符的数量
            # self.data_num_received += num
            # self.lineEdit.setText(str(self.data_num_received))

            # 获取到text光标
            # textCursor = self.textbox_receive.textCursor()
            # 滚动到底部
            # textCursor.movePosition(textCursor.MoveOperation.End)

            # self.textbox_receive.setTextCursor(textCursor)
            # self.pre_time = self.cur_time
            # self.cur_time = time.perf_counter()
            # execution_time = self.cur_time - self.pre_time  # 计算执行时间（秒）
            # print(f"执行时间: {execution_time} 秒, 字节数：{num_temp}, 效率：{num_temp/execution_time} byte/s")
        else:
            pass
    def save_data_to_csv(self, file_name="output.csv"):
        """将接收到的最新数据保存为 CSV 文件"""
        # 获取当前时间
        # current_time = time.localtime()  # 返回结构化的时间信息
        # 格式化时间为字符串
        # timestamp = time.strftime("%Y-%m-%d_%H-%M-%S", current_time)  # 格式: 年-月-日_小时-分钟-秒
        # 用时间戳命名文件
        # file_name = f"output_{timestamp}.csv"
        with self.data_lock:  # 确保线程安全
            data_to_save = self.parsed_data.copy()  # 复制数据以避免修改时出现冲突

        try:
            with open(file_name, mode='w', newline='') as file:
                writer = csv.writer(file)
                writer.writerow(["Value"])  # 写入表头
                for value in data_to_save:
                    writer.writerow([value])
            QMessageBox.information(self, "保存成功", f"数据已成功保存到 {file_name}")
        except Exception as e:
            QMessageBox.critical(self, "保存失败", f"保存数据时出错：{str(e)}")

    def trigger_save_data(self):
        """触发保存数据"""
        file_name, _ = QtWidgets.QFileDialog.getSaveFileName(self, "保存为 CSV 文件", "", "CSV Files (*.csv)")
        if file_name:
            self.save_data_to_csv(file_name)
    
    def trigger_change_sample_rate(self):
        self.sample_rate = 10000 // int(self.combo_sample_rate.currentText())
        self.sample_temp_count = 1

    def trigger_change_times(self):
        self.times = float(self.combo_times.currentText())

    def trigger_change_mode(self):
        self.mode = self.combo_mode.currentText()
        self.times = float(PySide6_Serial.times_map[self.mode])
        self.combo_times.setCurrentText(str(self.times))



